package cn.kmsoft.spatialboot.service.impl;

import cn.kmsoft.spatialboot.service.SpatialAnalysisService;
import cn.kmsoft.spatialboot.service.executor.DifferenceExecutor;

import cn.kmsoft.wololo.object.GeoJson;
import cn.kmsoft.wololo.object.GeoJsonFeature;
import cn.kmsoft.spatialboot.common.object.StatsFieldObject;
import cn.kmsoft.spatialboot.common.object.StatsObject;
import cn.kmsoft.spatialboot.common.object.StatsParams;
import cn.kmsoft.spatialboot.common.object.TaskParam;
import cn.kmsoft.wololo.object.query.AnalysisType;
import cn.kmsoft.wololo.object.query.GeoJsonQuery;
import cn.kmsoft.wololo.object.query.TableQuery;
import cn.kmsoft.spatialboot.config.dbswitch.DynamicSwitch;
import cn.kmsoft.spatialboot.utils.EllipsoidalAreaCalculatorEx;

import cn.kmsoft.stgeometry.util.GeoJsonUtil;
import cn.kmsoft.stgeometry.util.GeomUtil;
import com.alibaba.fastjson.JSONObject;
import cn.kmsoft.spatialboot.dao.LayerDifferenceDao;
import cn.kmsoft.spatialboot.dao.LayerIntersectDao;
import cn.kmsoft.spatialboot.dao.LayerSpatialQueryDao;
import cn.kmsoft.spatialboot.dao.SpatialStatDao;
import cn.kmsoft.spatialboot.service.executor.IntersectExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.ExecutionException;

import java.util.Map;


/**
 * @author wangq
 * @date 2019/7/4.
 */
@Service
//@Slf4j
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class SpatialAnalysisServiceImpl implements SpatialAnalysisService {
    private static final Logger logger = LoggerFactory.getLogger(SpatialAnalysisServiceImpl.class);

    private final LayerIntersectDao layerIntersectDao;
    private final LayerDifferenceDao layerDifferenceDao;
    private final SpatialStatDao spatialStatDao;
    private final LayerSpatialQueryDao layerSpatialQueryDao;

    private double centralMeridian=105.0;
    private String sourceEPSG="EPSG:4496";
    private String targetEPSG="EPSG:4490";

//    @Autowired
//    public SpatialAnalysisServiceImpl(LayerIntersectDao layerIntersectDao, LayerDifferenceDao layerDifferenceDao, SpatialStatDao spatialStatDao) {
//        this.layerIntersectDao = layerIntersectDao;
//        this.layerDifferenceDao = layerDifferenceDao;
//        this.spatialStatDao = spatialStatDao;
//    }
    @Autowired
    public SpatialAnalysisServiceImpl(LayerIntersectDao layerIntersectDao, LayerDifferenceDao layerDifferenceDao, SpatialStatDao spatialStatDao,LayerSpatialQueryDao layerSpatialQueryDao) {
        this.layerIntersectDao = layerIntersectDao;
        this.layerDifferenceDao = layerDifferenceDao;
        this.spatialStatDao = spatialStatDao;
        this.layerSpatialQueryDao= layerSpatialQueryDao;
    }


    @DynamicSwitch
    @Override
    public GeoJson[] spatialQueryByGeoJson(String dbType, String instanceName, String tableId, GeoJsonQuery query, String targetGeoType) throws ExecutionException, InterruptedException {
        TaskParam taskParam = new TaskParam();

        taskParam.setDbType(dbType);
        taskParam.setInstance(instanceName);
        taskParam.setDigits(query.getDigits());
        taskParam.setReturnEllipsoidalArea(query.getReturnEllipsoidalArea());
        AnalysisType analysisType = query.getType();//叠加分析、空间查询、擦除分析

        //是否需要进行投影坐标的转换
        if ("py2".equalsIgnoreCase(targetGeoType)) {
            taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerSpatialQueryDao.spatialQueryByGeoJsonWithCrsTransform(id, wkt, wkid, fgeo_wkid));
        } else if ("py".equalsIgnoreCase(targetGeoType)) {
            taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerSpatialQueryDao.spatialQueryByGeoJson(id, wkt, wkid));
        }
        taskParam.setTableId(tableId);
        taskParam.setWkid(query.getWkid());
        taskParam.setFgeo_wkid(query.getFgeo_wkid());

        logger.info(String.format("-->Fgeo_wkid：%d", query.getFgeo_wkid()));

        return new IntersectExecutor().executeTask(taskParam, query.getGeoJson());
    }

    @DynamicSwitch
    @Override
    public GeoJson[] intersectByGeoJson(String dbType, String instanceName, String tableId, GeoJsonQuery query, String targetGeoType) throws ExecutionException, InterruptedException {
        TaskParam taskParam = new TaskParam();

        taskParam.setDbType(dbType);
        taskParam.setInstance(instanceName);
        taskParam.setDigits(query.getDigits());
        taskParam.setReturnEllipsoidalArea(query.getReturnEllipsoidalArea());
        AnalysisType analysisType = query.getType();//叠加分析、空间查询、擦除分析

        //是否需要进行投影坐标的转换
        if ("py2".equalsIgnoreCase(targetGeoType)) {
            taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerIntersectDao.intersectByGeoJsonWithCrsTransform(id, wkt, wkid,fgeo_wkid));
        } else if ("py".equalsIgnoreCase(targetGeoType)) {
            taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerIntersectDao.intersectByGeoJson(id, wkt, wkid));
        } else if ("pl".equalsIgnoreCase(targetGeoType)) {
            taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerIntersectDao.intersectPolylineByGeoJson(id, wkt, wkid));
        } else if ("pt".equalsIgnoreCase(targetGeoType)) {
            taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerIntersectDao.intersectPointByGeoJson(id, wkt, wkid));
        }
        taskParam.setTableId(tableId);
        taskParam.setWkid(query.getWkid());
        taskParam.setFgeo_wkid(query.getFgeo_wkid());

        logger.info(String.format("-->Fgeo_wkid：%d", query.getFgeo_wkid()));


        return new IntersectExecutor().executeTask(taskParam, query.getGeoJson());
    }

    @DynamicSwitch
    @Override
    public GeoJson[] intersectByTable(String dbType,String instanceName, TableQuery query) {

        List<GeoJsonFeature> outFeatures = GeoJsonUtil.parse(
                layerIntersectDao.intersectByTable(query.getOverlaidTableId(), query.getOverlayTableId()), query.getWkid());
        GeoJson geo = new GeoJson();
        if (outFeatures.size() > 0) {
            geo.setType("FeatureCollection");
            geo.setFeatures(outFeatures);
        }

        if (query.getReturnEllipsoidalArea()) {
            addAreaToGeoJson(geo, query.getDigits());
        }

        return new GeoJson[]{geo};
    }

    @DynamicSwitch
    @Override
    public GeoJson[] differenceByGeoJson(String dbType,String instanceName, String tableId, GeoJsonQuery query) throws ExecutionException, InterruptedException {
        TaskParam taskParam = new TaskParam();

        taskParam.setDbType(dbType);
        taskParam.setInstance(instanceName);
        taskParam.setDigits(query.getDigits());
        taskParam.setReturnEllipsoidalArea(query.getReturnEllipsoidalArea());
        AnalysisType analysisType = query.getType();//叠加分析、空间查询、擦除分析

        //数据库st_geometry擦除操作
        taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerDifferenceDao.differenceByGeoJson(id, wkt, wkid));
        taskParam.setTableId(tableId);
        taskParam.setWkid(query.getWkid());
        taskParam.setFgeo_wkid(query.getFgeo_wkid());

        return new DifferenceExecutor().executeTask(taskParam, query.getGeoJson());
    }

    @DynamicSwitch
    @Override
    public GeoJson[] differenceByTable(String dbType,String instanceName, TableQuery query) throws ExecutionException, InterruptedException {
        List<GeoJsonFeature> outFeatures = GeoJsonUtil.parse(
                layerDifferenceDao.getOverlayTableById(query.getOverlaidTableId()), query.getWkid());
        GeoJson overlaidTable = new GeoJson();
        if (outFeatures.size() > 0) {
            overlaidTable.setType("FeatureCollection");
            overlaidTable.setFeatures(outFeatures);
        }

        TaskParam taskParam = new TaskParam();

        taskParam.setDbType(dbType);
        taskParam.setInstance(instanceName);
        taskParam.setDigits(query.getDigits());
        taskParam.setReturnEllipsoidalArea(query.getReturnEllipsoidalArea());

        //数据库擦除操作
        taskParam.setSpatialOperate((id, wkt, wkid, fgeo_wkid) -> layerDifferenceDao.differenceByGeoJson(id, wkt, wkid));
        taskParam.setTableId(query.getOverlayTableId());
        taskParam.setWkid(query.getWkid());
        taskParam.setFgeo_wkid(query.getFgeo_wkid());

        GeoJson[] diffResults = new DifferenceExecutor().executeTask(taskParam, JSONObject.toJSONString(overlaidTable));
        return diffResults;
    }

    /**
     * 给GeoJson的Properties添加椭球面积
     *
     * @param geoJson 待处理geoJson
     */
    private void addAreaToGeoJson(GeoJson geoJson, int digits) {
        EllipsoidalAreaCalculatorEx calculator = new EllipsoidalAreaCalculatorEx(centralMeridian, sourceEPSG, targetEPSG);
        geoJson.getFeatures().forEach(c -> c.getProperties().put("TQMJ", calculator.getFeatureArea(GeomUtil.geojsonToWkt(JSONObject.toJSONString(c.getGeometry())), digits)));
    }

    /**
     * 给GeoJson的Properties添加平面面积
     *
     * @param geoJson 待处理geoJson
     */
    private void addAreaToGeoJson2(GeoJson geoJson, int digits) {
        EllipsoidalAreaCalculatorEx calculator = new EllipsoidalAreaCalculatorEx(centralMeridian, sourceEPSG, targetEPSG);
        geoJson.getFeatures().forEach(c -> c.getProperties().put("PMMJ", calculator.getFeatureArea2(GeomUtil.geojsonToWkt(JSONObject.toJSONString(c.getGeometry())), digits)));
    }

    /**
     * 统计字段输出别名
     *
     * @param fieldName 字段名
     * @return
     */
    private String getAsField(String fieldName) {
        return fieldName.replaceAll("\\.", "_").replaceAll("\"", "_");
    }

    @DynamicSwitch
    @Override
    public List<Map<String, Object>> stat(String instanceName, StatsParams statsParams) {
        long l = System.currentTimeMillis();

        List<Map<String, Object>> result = spatialStatDao.stat(parse(statsParams));

        //log.info("统计耗时：{}", System.currentTimeMillis() - l);

        return result;
    }

    /**
     * 转化统计参数
     *
     * @param statsParams 统计参数
     * @return 统计查询对象
     */
    private StatsObject parse(StatsParams statsParams) {

        StatsObject statsObj = new StatsObject();
        statsObj.setGroupFields(statsParams.getGroupFields());

        List<StatsFieldObject> statsFields = statsParams.getStatsFields();
        for (StatsFieldObject statsField : statsFields) {
            String outStatsFieldName = statsField.getAliasName();
            String asStatisticFieldName = getAsField(outStatsFieldName);
            statsField.setAliasName(asStatisticFieldName);
            statsObj.getStatsFields().add(statsField);
            statsObj.getFieldMapping().put(outStatsFieldName, asStatisticFieldName);
        }

        statsObj.setTableId(statsParams.getTableId());
        statsObj.setWhere(statsParams.getWhere());
        statsObj.setWkt(statsParams.getGeometry());
        statsObj.setWkid(statsParams.getWkid());
        return statsObj;
    }
}
